/******************************************************************************/
/*                                                                            */
/*  Copyright (c) 2009: ngi-central.org                                       */
/*  All rights reserved.                                                      */
/*                                                                            */
/******************************************************************************/


/// \file
/// This file implements the polygon geometric primitive.

#ifndef __sgl_polygon_h__
#define __sgl_polygon_h__

#include <sgl/global_settings.h>
#include <sgl/conditions.h>
#include <sgl/geometric_primitive.h>
#include <sgl/point.h>
#include <sgl/triangle.h>
#include <sgl/box.h>
#include <sgl/quadrilateral.h>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/static_assert.hpp>
#include <boost/operators.hpp>
#include <boost/concept_check.hpp>

namespace sgl {


/******************************************************************************/
/*                                                                            */
/* polygon                                                                    */
/*                                                                            */
/******************************************************************************/

template <
	/// The dimensionality. 
	//!
	//! This defaults to two dimensions.
	size_t D=2,
	/// The element type. 
	//! 
	//! This type defaults to SGL_FPTYPE (which in turn defaults to double), 
	//! but any other numeric type is also suitable.
	typename T=SGL_FPTYPE,
	/// The container template type.
	typename C=std::vector<sgl::point<D,T> > >
/// A polygon in euclidean space.
//!
//! A polygon can be seen as sequence of connected line_segments.
class polygon
	/// A polygon is a geometric primitive.
	: public geometric_primitive<D,T>
	, public C
	, boost::equality_comparable<sgl::polygon<D,T>, sgl::polygon<D,T>
	, boost::additive<sgl::polygon<D,T>, sgl::vector<D,T>
	> >
{
	// A polygon must have two or more dimensions.
	BOOST_STATIC_ASSERT(D >= 2);

	/// The container type.
	typedef C container_type;

public:
	/// Default constructor.
	//!
	//! Constructs a zero polygon.
	polygon()
		: C()
	{ }

	template<
		/// The container type.
		typename C2>
	/// Construct a polygon from points contained in a STL compatible
	/// container.
	polygon(
		/// A const reference to the container.
		C2 const& container)
		: C()
	{ 
		std::copy(container.begin(), container.end(), 
			std::back_inserter(*this));
	}

	/// Construct a polygon from a triangle.
	polygon(
		/// A const reference to the triangle.
		sgl::triangle<D,T> const& triangle)
		: C()
	{
		push_back(triangle.p1());
		push_back(triangle.p2());
		push_back(triangle.p3());
	}

	/// Construct a polygon from a box.
	polygon(
		/// A const reference to the triangle.
		sgl::box<2,T> const& box)
		: C()
	{
		push_back(box.origin());
		push_back(box.origin() + sgl::vector<2,T>(box.size().dx(), T(0)));
		push_back(box.origin() + box.size());
		push_back(box.origin() + sgl::vector<2,T>(T(0), box.size().dy()));
	}

	/// Construct a polygon from a quadrilateral.
	polygon(
		/// A const reference to the quadrilateral
		sgl::quadrilateral<D,T> const& quadrilateral)
		: C()
	{
		push_back(quadrilateral.p1());
		push_back(quadrilateral.p2());
		push_back(quadrilateral.p3());
		push_back(quadrilateral.p4());
	}

	/// Copy constructor.
	polygon(
		/// The right hand side.
		sgl::polygon<D,T> const& rhs)
		: C(rhs)
	{ }

	/// Copy assignment operator.
	sgl::polygon<D,T> & operator = (
		/// The right hand side.
		sgl::polygon<D,T> rhs)
	{
		swap(rhs);
		return *this;
	}

	/// Exchanges the elements of two polygon objects.
	void swap(
		/// The other polygon.
		sgl::polygon<D,T> & rhs)
	{
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		std::swap((C &)(*this), (C &)(rhs));
	}

	/// Find the type of the geometric primitive.
	//!
	//! /return The type of the primitive.
	geometric_type type() const
	{
		return sgl::geometric_primitive<D,T>::polygon;
	} 

	/// Equality operator.
	//!
	//! Two polygons are compared for equality by comparing their vertices in a 
	//! pairwise manner. The polygons are equal only if all pairwise 
	//! comparisons are equal.
	//!
	//! Through the use of boost::equality as a base class operator != is 
	//! defined as well and implemented in terms of this operator ==.
	//!
	//! \return true if equal, false otherwise.
    bool operator == (
		/// Right hand side.
		sgl::polygon<D,T> const& rhs) const
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		return 
			((C const&)(*this) == (C const&)(rhs));
    }

	/// Add a vector to a polygon.
	//!
	//! Adding a vector to a polygon moves the polygon in direction of the 
	//! vector. Addition is implemented by pairwise addition of the components 
	//! of the polygons vertices and the vector.
	//!
	//! Through the use of boost::additive as a base class operator + is 
	//! defined as well and implemented in terms of this operator +=.
	//!
	//! \return A reference to this object.
    sgl::polygon<D,T> & operator += (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		std::for_each(begin(), end(), 
			boost::phoenix::arg_names::arg1 += rhs);

        return *this;
    }

	/// Subtract a vector from a polygon.
	//!
	//! Subtracting a vector from a polygon moves the polygon in the opposite 
	//! direction of the vector. Subtraction is implemented by pairwise 
	//! subtraction of the components of the polygons vertices and the vector.
	//!
	//! Through the use of boost::additive as a base class operator - is 
	//! defined as well and implemented in terms of this operator -=.
	//!
	//! \return A reference to this object.
    sgl::polygon<D,T> & operator -= (
		/// Right hand side.
		sgl::vector<D,T> const& rhs)
    {
		SGL_PRECONDITION(D == rhs.dimensions(),
			"dimension mismatch.");

		std::for_each(begin(), end(), 
			boost::phoenix::arg_names::arg1 -= rhs);

		return *this;
    }
};


/// A two-dimensional polygon with the default component type.
typedef sgl::polygon<2> polygon2;

/// A two-dimensional polygon with float component type.
typedef sgl::polygon<2, float> polygon2f;

/// A two-dimensional polygon with double component type.
typedef sgl::polygon<2, double> polygon2d;


/// A three-dimensional polygon with the default component type.
typedef sgl::polygon<3> polygon3;

/// A three-dimensional polygon with float component type.
typedef sgl::polygon<3, float> polygon3f;

/// A three-dimensional polygon with double component type.
typedef sgl::polygon<3, double> polygon3d;


} // namespace sgl

#endif // __sgl_polygon_h__
